import xml.etree.ElementTree as ET  
from xml.etree.ElementTree import SubElement

from ArxmlBasicTools.Arxml import arxmlFile

class runnableConfig(object):
    def __init__(self,RunnableName: str ='RunnableNameNodefine') -> None:
        self.RunnableName = RunnableName
        self.runnableHeader = 'no define'
        self.dataAccessHeader = 'no define'
        self.severCallHeader = 'no define'

class SwcComponentConfig(object):
        
    path = '/Components'
    name = 'no define'
    element = 'no define'
    portsHeader = 'no define'
    internalBehaviorsHeader = 'no define'
    RunnableDict = dict()

    def __init__(self,path,name):
        self.path = path
        self.name = name
        self.element = ET.Element("APPLICATION-SW-COMPONENT-TYPE")
        SubElement(self.element,'SHORT-NAME').text = name  

    def createComponent_el(self,path,name):
        self.path = path
        self.name = name
        self.element = ET.Element("APPLICATION-SW-COMPONENT-TYPE")
        SubElement(self.element,'SHORT-NAME').text = name  
    
    def addSwcCSRPort(self,swcPortName:str,CS_Path_Name:str):
        if self.portsHeader == 'no define':
            self.portsHeader = SubElement(self.element,'PORTS')

        RPort_el = SubElement(self.portsHeader,'R-PORT-PROTOTYPE')
        SubElement(RPort_el,'SHORT-NAME').text = swcPortName
        SubElement(RPort_el,'REQUIRED-INTERFACE-TREF', attrib={'DEST': 'CLIENT-SERVER-INTERFACE'}).text = CS_Path_Name

    def addRunnable(self,RE_Name:str):
        if self.internalBehaviorsHeader == 'no define':
            self.internalBehaviorsHeader = SubElement(SubElement(self.element,'INTERNAL-BEHAVIORS'),'SWC-INTERNAL-BEHAVIOR')
            SubElement(self.internalBehaviorsHeader,'SHORT-NAME').text = self.name + '_IB'
        
        RUNNABLES_el = SubElement(self.internalBehaviorsHeader,'RUNNABLES')
        RE_el = SubElement(RUNNABLES_el,'RUNNABLE-ENTITY')
        SubElement(RE_el,'SHORT-NAME').text = RE_Name

        new_runnable = runnableConfig(RE_Name)
        new_runnable.runnableHeader = RE_el
        self.RunnableDict[RE_Name] = new_runnable

    def addServerCall(self,RE_Name:str,swcPortName:str,CSO_Path_Name:str):
        if (RE_Name in self.RunnableDict) == False:
            self.addRunnable(RE_Name)
            
        if self.RunnableDict[RE_Name].severCallHeader == 'no define':
            self.RunnableDict[RE_Name].severCallHeader = \
                SubElement(self.RunnableDict[RE_Name].runnableHeader,'SERVER-CALL-POINTS')

        CSO_Name_l = CSO_Path_Name.split('/')
        CSO_Name = CSO_Name_l[len(CSO_Name_l) - 1]
        SYNCHRONOUS_SEVER_CALL_el = SubElement(self.RunnableDict[RE_Name].severCallHeader,'SYNCHRONOUS-SERVER-CALL-POINT')
        SubElement(SYNCHRONOUS_SEVER_CALL_el,'SHORT-NAME').text = 'SyncServerCall_' + swcPortName + '_' + CSO_Name
        OPERATION_IREF_el = SubElement(SYNCHRONOUS_SEVER_CALL_el,'OPERATION-IREF')
        SubElement(OPERATION_IREF_el,'CONTEXT-R-PORT-REF',attrib={'DEST': 'R-PORT-PROTOTYPE'}).text = self.path + '/' + self.name + '/' + swcPortName
        SubElement(OPERATION_IREF_el,'TARGET-REQUIRED-OPERATION-REF',attrib={'DEST': 'CLIENT-SERVER-OPERATION'}).text = CSO_Path_Name


class SwcArxmlFile(arxmlFile):
    def __init__(self) -> None:
        super(SwcArxmlFile, self).__init__()
        self.element = 'no define'
        self.componentDict = dict()

    def createSwcArxmlFile(self,PackagePath):
        self.element = self.createArxmlPackage(PackagePath)

    def addComponent(self,Component: SwcComponentConfig):
        self.componentDict[Component.path + '/' + Component.name] = Component
        self.element.append(Component.element)

    def paraseSwcArxmlFile(self,arxmlFileName,PackagePath):
        self.paraseArxmlFile(arxmlFileName)
        self.element = self.findPackageWithName(PackagePath)
        self.findComponent_inPackage(self.element,PackagePath)

        if len(self.componentDict) > 0:
            for component in self.componentDict:
                self.findPortsHeader_inComponent(self.componentDict[component])
                self.findInternalBehaviorsHeader_inComponent(self.componentDict[component])

    def findComponent_inPackage(self,package,path):
        for child in package:  
            if child.tag == 'APPLICATION-SW-COMPONENT-TYPE':
                AppComponentEl_ = child
                for child in AppComponentEl_:
                    if child.tag == 'SHORT-NAME' :
                        AppComponentName = child.text
                        component = SwcComponentConfig(path,AppComponentName)

                        component.element = AppComponentEl_

                        self.componentDict[component.path + '/' + component.name] = component

    def findPortsHeader_inComponent(self,component: SwcComponentConfig):
        for child in component.element:
            if child.tag == 'PORTS':
                component.portsHeader = child
                return 

    #find the first Internal Behavior
    def findInternalBehaviorsHeader_inComponent(self,component: SwcComponentConfig):
        for child in component.element:
            if child.tag == 'INTERNAL-BEHAVIORS':
                for child_ in child:
                    if child_.tag == 'SWC-INTERNAL-BEHAVIOR':
                        component.internalBehaviorsHeader = child_
                        self.findRunnableEntry_inIB(component) 
    
    def findRunnableEntry_inIB(self,component: SwcComponentConfig):
        runnables_el = 'no define'
        for child in component.internalBehaviorsHeader:
            if child.tag == 'RUNNABLES':
                runnables_el = child
                break
        
        if runnables_el != 'no define':
            for child in runnables_el:
                if child.tag == 'RUNNABLE-ENTITY':
                    RE_el = child
                    for child in RE_el:
                        if child.tag == 'SHORT-NAME':
                            RE_Name = child.text
                            new_runnable = runnableConfig(RE_Name)
                            new_runnable.runnableHeader = RE_el
                            component.RunnableDict[RE_Name] = new_runnable
